
// ===============================================================================================================
// -*- C++ -*-
//
// ProceduralTerrain.cpp - A proceduraly generated terrain that is the main game level.
//
// Copyright (c) 2011 Guilherme R. Lampert
// guilherme.ronaldo.lampert@gmail.com
//
// This code is licenced under the MIT license.
//
// This software is provided "as is" without express or implied
// warranties. You may freely copy and compile this source into
// applications you distribute provided that the copyright text
// above is included in the resulting source code.
//
// ===============================================================================================================

#include <ProceduralTerrain.hpp>
#include <Math.hpp> // For the random numbers...

// =========================================================
// ProceduralTerrain Class Implementation
// =========================================================

ProceduralTerrain::ProceduralTerrain(int size_x, int size_z, int scaleFactor, int noiseFactor, const Texture * tex, const std::string & levelName)
: texture(tex), sizeX(size_x), sizeZ(size_z), scale(scaleFactor), indexArray(0), vertexArray(0), texCoordArray(0)
{
	name = levelName;

	if (texture)
	{
		texture->AddRef();
	}

	const int rows = (sizeX * sizeZ);

	// Time for some mallocs:

	indexArray = reinterpret_cast<unsigned int *>(MemAlloc(rows * 2 * sizeof(unsigned int)));

	vertexArray = reinterpret_cast<float(*)[3]>(MemAlloc(rows * 3 * sizeof(float)));

	texCoordArray = reinterpret_cast<float(*)[2]>(MemAlloc(rows * 2 * sizeof(float)));

	if (!Fail()) // If ok continue:
	{
		int x, z;
		int index = 0;
		int currentVertex = 0;

		Math::SeedRandomGenerator();

		for (z = 0; z < sizeZ; ++z)
		{
			for (x = 0; x < sizeX; ++x)
			{
				// Generate vertices and texture coordinates for the height field:

				vertexArray[x + z * sizeX][0] =  static_cast<float>(x * scale);
				vertexArray[x + z * sizeX][1] = (5.0f + Math::UniformRandom() * noiseFactor);
				vertexArray[x + z * sizeX][2] = -static_cast<float>(z * scale);

				currentVertex = (z * sizeX + x);

				texCoordArray[currentVertex][0] = static_cast<float>(x);
				texCoordArray[currentVertex][1] = static_cast<float>(z);
			}
		}

		for (z = 0; z < (sizeZ - 1); ++z)
		{
			for (x = 0; x < sizeX; ++x)
			{
				// Now make the vertex indices:

				currentVertex = (z * sizeX + x);
				indexArray[index++] = (currentVertex + sizeX);
				indexArray[index++] = currentVertex;
			}
		}
	}
}

ProceduralTerrain::~ProceduralTerrain(void)
{
	// Cleanup the mess:

	if (texture != 0)
	{
		texture->Release();
	}

	MemFree(indexArray);
	MemFree(vertexArray);
	MemFree(texCoordArray);
}

float ProceduralTerrain::GetHeightAt(float x, float z) const
{
	const float projCameraX = ( x / scale);
	const float projCameraZ = (-z / scale);

	int hflCol0 = static_cast<int>(projCameraX);
	int hflRow0 = static_cast<int>(projCameraZ);
	int hflCol1 = (hflCol0 + 1);
	int hflRow1 = (hflRow0 + 1);

	float h00 = vertexArray[hflCol0 + hflRow0 * sizeX][1];
	float h01 = vertexArray[hflCol1 + hflRow0 * sizeX][1];
	float h11 = vertexArray[hflCol1 + hflRow1 * sizeX][1];
	float h10 = vertexArray[hflCol0 + hflRow1 * sizeX][1];

	float tx = (projCameraX - static_cast<float>(hflCol0));
	float ty = (projCameraZ - static_cast<float>(hflRow0));
	float txty = (tx * ty);

	return (h00 * (1.0f - ty - tx + txty) + h01 * (tx - txty) + h11 * txty + h10 * (ty - txty));
}

float ProceduralTerrain::GetScale(void) const
{
	return (static_cast<float>(scale));
}

void ProceduralTerrain::GetExtents(int & max_x, int & max_z) const
{
	max_x = sizeX;
	max_z = sizeZ;
}

GameLevel::Type ProceduralTerrain::GetLevelType(void) const
{
	return (GameLevel::PROCEDURAL_TERRAIN);
}

bool ProceduralTerrain::Fail(void) const
{
	return ((indexArray == 0) || (vertexArray == 0) || (texCoordArray == 0));
}